package server;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.nio.file.Files;
import java.util.InputMismatchException;

/**
 * @file Client_Handler.java
 *
 * @version 0.4
 * @author James Dibble 10009689
 */
public class Client_Handler implements Runnable {
    // <editor-fold defaultstate="collapsed" desc="Attributes">

    private Client_State state;
    private Socket sSocket;
    private final String TERMS_OF_SERVICE = "Terms of reference. Do you accept? Y or N";
    private final String ANOTHER = "Another? Y or N";
    private final String INVALID = "Invalid input --- ";
    private final String SENDING = "FILE_INCOMING:";
    private final String SENT = "SENT";
    private PrintWriter pwClientOutput;
    private Server parent;
    public boolean bolDebug;
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Constructors">
    public Client_Handler(Socket sSocket, Server parent, boolean bolDebug) throws IOException {
        this.state = Client_State.TERMS_OF_REFRENCE;
        this.sSocket = sSocket;
        this.pwClientOutput = new PrintWriter(sSocket.getOutputStream(), true);
        this.parent = parent;
        this.bolDebug = bolDebug;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Methods">
    @Override
    public void run() {
        try {
            BufferedReader brClientInput = new BufferedReader(new InputStreamReader(sSocket.getInputStream()));

            this.SendToClient(TERMS_OF_SERVICE);
            String strClientInput;
            while ((strClientInput = brClientInput.readLine()) != null
                    && sSocket.isConnected()
                    && state != Client_State.CLOSED) {
                System.out.println(String.format("%s: %s",
                        sSocket.getInetAddress().getHostName(),
                        strClientInput));
                if (state == Client_State.TERMS_OF_REFRENCE) {
                    if (strClientInput.equalsIgnoreCase("y")) {
                        this.SendToClient(parent.listResources());
                        state = Client_State.FILE_LIST;
                    } else if (strClientInput.equalsIgnoreCase("n")) {
                        sSocket.close();
                        state = Client_State.CLOSED;
                    } else {
                        this.SendToClient(this.INVALID + this.TERMS_OF_SERVICE);
                    }
                } else if (state == Client_State.FILE_LIST) {
                    try {
                        int intTemp = Integer.parseInt(strClientInput);
                        Resource thisResource = null;
                        if ((thisResource = parent.getResourceByID(intTemp)) != null) {
                            if (this.parent.getServerType() == Server.Server_Type.DESCRIPTION_SERVER) {
                                thisResource.UploadResourceAsString();
                                this.SendToClient(thisResource.toString());
                            } else if (this.parent.getServerType() == Server.Server_Type.FILE_SERVER) {
                                thisResource.UploadResourceAsFile();
                                this.SendResourceToClient(thisResource);
                            }
                            this.SendToClient(ANOTHER);
                            this.state = Client_State.ANOTHER;
                        } else {
                            this.SendToClient(this.INVALID + parent.listResources());
                        }
                    } catch (InputMismatchException imEx) {
                        this.SendToClient(this.INVALID + parent.listResources());
                    } catch (NumberFormatException nfEx){
                        this.SendToClient(this.INVALID + parent.listResources());
                    }
                } else if (state == Client_State.ANOTHER) {
                    if (strClientInput.equalsIgnoreCase("y")) {
                        this.SendToClient(parent.listResources());
                        state = Client_State.FILE_LIST;
                    } else if (strClientInput.equalsIgnoreCase("n")) {
                        sSocket.close();
                        state = Client_State.CLOSED;
                    } else {
                        this.SendToClient(this.INVALID + this.ANOTHER);
                    }
                }
            }
            System.out.println(String.format("%s has disconnected.",
                    sSocket.getInetAddress().getHostName()));
        } catch (IOException ex) {
            if(bolDebug)ex.printStackTrace(System.out);
            System.out.println(String.format("Error reading from %s",
                    sSocket.getInetAddress().getHostName()));
        }
    }

    public String getHostName() {
        return sSocket.getInetAddress().getHostName();
    }

    public void SendToClient(String message) {
        System.out.println(String.format("Server->%s: %s",
                sSocket.getInetAddress().getHostName(),
                message));
        pwClientOutput.println(message);
        pwClientOutput.flush();
    }

    public void SendResourceToClient(Resource thisResource) throws SocketException, FileNotFoundException, IOException {
        this.SendToClient(SENDING + (int) thisResource.getResourceAsFile().length());
        this.SendToClient(SENDING + thisResource.getFileName());
        
        File thisFile = thisResource.getResourceAsFile();
        byte[] byteBuff = new byte[(int)thisFile.length()];
        byteBuff = Files.readAllBytes(thisFile.toPath());
        sSocket.getOutputStream().flush();
        sSocket.getOutputStream().write(byteBuff);
        sSocket.getOutputStream().flush();
        System.out.println(String.format("Server->%s: %s",
                sSocket.getInetAddress().getHostName(),
                "File uploaded."));
    }
    // </editor-fold>

    public enum Client_State {
        TERMS_OF_REFRENCE, FILE_LIST, ANOTHER, CLOSED
    }
}